package com.trip.back.stage.common.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import com.trip.back.stage.common.exception.AuthExceptionEntryPoint;
import com.trip.back.stage.common.exception.CustomAccessDeniedHandler;
import com.trip.back.stage.common.exception.CustomWebResponseExceptionTranslator;

/**
 * oauth2配置
 */
@Configuration
public class OAuth2ServerConfig {

	private static final String RESOURCE_ID = "api";
	
	
	@Configuration
	@EnableResourceServer
	protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

		@Override
		public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
			resources
				.resourceId(RESOURCE_ID)
				.stateless(true)
				.authenticationEntryPoint(new AuthExceptionEntryPoint()) // tokan校验失败返回信息
		        .accessDeniedHandler(new CustomAccessDeniedHandler()); // 授权失败(forbidden)时返回信息
		}

		@Override
		public void configure(HttpSecurity http) throws Exception {
			// 配置哪些接口需要认证、哪些接口无须认证
			http.authorizeRequests()
				// 无须认证的
				.antMatchers("/login").permitAll() // 登录
				.antMatchers("/login.html").permitAll() // 登录
				.antMatchers("/sys/user/forgetPassword").permitAll() // 忘记密码
				// 需要认证的
				.antMatchers("/sys/*").authenticated();
		}
		
		@Configuration
	    @EnableAuthorizationServer
	    protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
			@Autowired
	        private AuthenticationManager authenticationManager;
	        @Autowired
	        private RedisConnectionFactory redisConnectionFactory;
	        
			@Override
			public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
				security.allowFormAuthenticationForClients(); // 允许表单认证
			}
			
			@Override
			public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
				String finalSecret = "{bcrypt}" + new BCryptPasswordEncoder().encode("GRKHLpx1");
	            //配置两个客户端,一个用于password认证一个用于client认证
	            clients.inMemory()
	                    .withClient("client_1")
	                    .resourceIds(RESOURCE_ID)
	                    .authorizedGrantTypes("client_credentials", "refresh_token")
	                    .scopes("select")
	                    .authorities("client")
	                    .secret(finalSecret)
	                    .accessTokenValiditySeconds(3600) // access token 有效时间3600秒
	                    .and()
	                    .withClient("client_2")
	                    .resourceIds(RESOURCE_ID)
	                    .authorizedGrantTypes("password", "refresh_token")
	                    .scopes("select")
	                    .authorities("oauth2")
	                    .secret(finalSecret)
	                    .accessTokenValiditySeconds(3600) // access token 有效时间3600秒
	                    ;
			}
			
			@Override
			public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
				endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
						.authenticationManager(authenticationManager)
						.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE);
				endpoints.exceptionTranslator(new CustomWebResponseExceptionTranslator());
			}
	        
		}
	}
	
}
